using System.Windows;
using System.Windows.Input;
using System.Windows.Shell;

namespace MetroWPF.Windows
{
    /// <summary>
    /// Provides the ability to create, configure, show, and manage the lifetime of windows and dialog boxes.
    /// </summary>
    public class Window : System.Windows.Window
    {
        /// <summary>
        /// Identifies the CloseCommand dependency property.
        /// </summary>
        public static readonly DependencyProperty CloseCommandProperty =
            DependencyProperty.Register("CloseCommand", typeof(ICommand), typeof(Window), new PropertyMetadata(default(ICommand)));

        /// <summary>
        /// Identifies the IsCloseButtonEnabled dependency property.
        /// </summary>
        public static readonly DependencyProperty IsCloseButtonEnabledProperty =
            DependencyProperty.Register("IsCloseButtonEnabled", typeof(bool), typeof(Window), new PropertyMetadata(true, OnCommandEnabledChanged));

        /// <summary>
        /// Identifies the IsMaximizeButtonEnabled dependency property.
        /// </summary>
        public static readonly DependencyProperty IsMaximizeButtonEnabledProperty =
            DependencyProperty.Register("IsMaximizeButtonEnabled", typeof(bool), typeof(Window), new PropertyMetadata(true, OnCommandEnabledChanged));

        /// <summary>
        /// Identifies the IsMinimizeButtonEnabled dependency property.
        /// </summary>
        public static readonly DependencyProperty IsMinimizeButtonEnabledProperty =
            DependencyProperty.Register("IsMinimizeButtonEnabled", typeof(bool), typeof(Window), new PropertyMetadata(true, OnCommandEnabledChanged));

        /// <summary>
        /// Identifies the MaximizeCommand dependency property.
        /// </summary>
        public static readonly DependencyProperty MaximizeCommandProperty =
            DependencyProperty.Register("MaximizeCommand", typeof(ICommand), typeof(Window), new PropertyMetadata(default(ICommand)));

        /// <summary>
        /// Identifies the MinimizeCommand dependency property.
        /// </summary>
        public static readonly DependencyProperty MinimizeCommandProperty =
            DependencyProperty.Register("MinimizeCommand", typeof(ICommand), typeof(Window), new PropertyMetadata(default(ICommand)));

        /// <summary>
        /// Identifies the TitleBarHeight dependency property.
        /// </summary>
        public static readonly DependencyProperty TitleBarHeightProperty =
            DependencyProperty.Register("TitleBarHeight", typeof(double), typeof(Window), new PropertyMetadata(default(double), OnTitleBarHeightChanged));

        /// <summary>
        /// Identifies the Title dependency property.
        /// </summary>
        public static readonly DependencyProperty TitleBarProperty =
            DependencyProperty.Register("TitleBar", typeof(object), typeof(Window), new PropertyMetadata(default(object)));

        /// <summary>
        /// The resource key of the Metro window style.
        /// </summary>
        private const string MetroWindowResourceKey = "MetroWindow";

        /// <summary>
        /// Initializes a new instance of the <see cref="Window"/> class.
        /// </summary>
        public Window()
            : base()
        {
            var style = Application.Current.Resources[MetroWindowResourceKey] as Style;
            if (style != null)
            {
                this.Style = style;
            }
            else
            {
                throw new ResourceReferenceKeyNotFoundException("Metro window style resource not found.", MetroWindowResourceKey);
            }
            this.UpdateCommands();
        }

        /// <summary>
        /// Gets the close command of the window.
        /// </summary>
        /// <value>
        /// The close command.
        /// </value>
        public ICommand CloseCommand
        {
            get { return (ICommand)GetValue(CloseCommandProperty); }
            private set { SetValue(CloseCommandProperty, value); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the close button of this window is enabled.
        /// </summary>
        /// <value>
        /// <c>true</c> if the close button of this instance is enabled; otherwise, <c>false</c>.
        /// </value>
        public bool IsCloseButtonEnabled
        {
            get { return (bool)GetValue(IsCloseButtonEnabledProperty); }
            set { SetValue(IsCloseButtonEnabledProperty, value); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the maximize button of this window is enabled.
        /// </summary>
        /// <value>
        /// <c>true</c> if the maximize button of this instance is enabled; otherwise, <c>false</c>.
        /// </value>
        public bool IsMaximizeButtonEnabled
        {
            get { return (bool)GetValue(IsMaximizeButtonEnabledProperty); }
            set { SetValue(IsMaximizeButtonEnabledProperty, value); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the minimize button of this window is enabled.
        /// </summary>
        /// <value>
        /// <c>true</c> if the minimize button of this instance is enabled; otherwise, <c>false</c>.
        /// </value>
        public bool IsMinimizeButtonEnabled
        {
            get { return (bool)GetValue(IsMinimizeButtonEnabledProperty); }
            set { SetValue(IsMinimizeButtonEnabledProperty, value); }
        }

        /// <summary>
        /// Gets the maximize command of the window.
        /// </summary>
        /// <value>
        /// The maximize command.
        /// </value>
        public ICommand MaximizeCommand
        {
            get { return (ICommand)GetValue(MaximizeCommandProperty); }
            private set { SetValue(MaximizeCommandProperty, value); }
        }

        /// <summary>
        /// Gets the minimize command of the window.
        /// </summary>
        /// <value>
        /// The minimize command.
        /// </value>
        public ICommand MinimizeCommand
        {
            get { return (ICommand)GetValue(MinimizeCommandProperty); }
            private set { SetValue(MinimizeCommandProperty, value); }
        }

        /// <summary>
        /// Gets or sets a window's title.
        /// </summary>
        /// <returns>A <see cref="T:System.Object" /> that contains the window's title.</returns>
        public object TitleBar
        {
            get { return (object)GetValue(TitleBarProperty); }
            set { SetValue(TitleBarProperty, value); }
        }

        /// <summary>
        /// Gets or sets the height of the title bar.
        /// </summary>
        /// <value>
        /// The height of the title bar.
        /// </value>
        public double TitleBarHeight
        {
            get { return (double)GetValue(TitleBarHeightProperty); }
            set { SetValue(TitleBarHeightProperty, value); }
        }

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate" />.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.UpdateCommands();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Window.StateChanged" /> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
        protected override void OnStateChanged(System.EventArgs e)
        {
            base.OnStateChanged(e);
            this.UpdateCommands();
        }

        /// <summary>
        /// Called when <see cref="IsCloseButtonEnabled"/>, <see cref="IsMaximizeButtonEnabled"/> or <see cref="IsMinimizeButtonEnabled"/> is changed.
        /// </summary>
        /// <param name="d">The dependency objecy that property is changed.</param>
        /// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnCommandEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = d as Window;
            if (window != null)
            {
                window.UpdateCommands();
            }
        }

        /// <summary>
        /// Called when the <see cref="TitleBarHeight"/> is changed.
        /// </summary>
        /// <param name="d">The dependency object that TitleBar is changed.</param>
        /// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnTitleBarHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = d as Window;
            if (window != null)
            {
                WindowChrome.SetWindowChrome(window,
                    new WindowChrome() { CaptionHeight = window.TitleBarHeight, UseAeroCaptionButtons = false });
            }
        }

        /// <summary>
        /// Updates the commands.
        /// </summary>
        private void UpdateCommands()
        {
            this.CloseCommand = this.GetCloseCommand();
            this.MaximizeCommand = this.GetMaximizeCommand();
            this.MinimizeCommand = this.GetMinimizeCommand();
        }
    }
}